﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data;
using System.Reflection.Emit;

namespace JinianNet.Reflection
{
    public class EntityHelper
    {
        private static readonly MethodInfo GetValueMethod;
        private static readonly MethodInfo IsDBNullMethod;
        private delegate T GetEntity<T>(IDataReader rd);

        static EntityHelper()
        {
            GetValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
            IsDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
        }

        public T PopulateEntity<T>(IDataReader collection) where T : new()
        {
            return PopulateEntity<T>(new T(), collection);
        }

        public T PopulateEntity<T>(T entity, IDataReader collection) where T : new()
        {
            if (entity == null)
            {
                entity = new T();
            }
            Type type = typeof(T);
            PropertyInfo[] propertys = type.GetProperties();
            foreach (PropertyInfo item in propertys)
            {
                if (collection[item.Name] != null && collection[item.Name]!=DBNull.Value)
                {
                    item.SetValue(entity, Convert.ChangeType(collection[item.Name], item.PropertyType), null);
                }
            }
            return entity;
        }

        public T PopulateEntity<T>(DataRow collection) where T : new()
        {
            return PopulateEntity<T>(new T(), collection);
        }

        public T PopulateEntity<T>(T entity, DataRow collection) where T : new()
        {
            if (entity == null)
            {
                entity = new T();
            }
            Type type = typeof(T);
            PropertyInfo[] propertys = type.GetProperties();
            foreach (PropertyInfo item in propertys)
            {
                if (collection.Table.Columns.Contains(item.Name))
                {
                    item.SetValue(entity, Convert.ChangeType(collection[item.Name], item.PropertyType), null);
                }
            }
            return entity;
        }

        public List<T> PopulateEntityCollection<T>(IDataReader collection) where T : new()
        {

            List<T> list = new List<T>();
            Type t = typeof(T);

            DynamicMethod method = new DynamicMethod("DynamicCreateEntity", t, new Type[] { typeof(IDataReader) }, t, true);
            ILGenerator generator = method.GetILGenerator();
            LocalBuilder result = generator.DeclareLocal(t);
            generator.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);

            for (int i = 0; i < collection.FieldCount; i++)
            {
                PropertyInfo pi = t.GetProperty(collection.GetName(i));
                System.Reflection.Emit.Label endIfLabel = generator.DefineLabel();
                if (pi != null && pi.GetSetMethod() != null)
                {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, IsDBNullMethod);
                    generator.Emit(OpCodes.Brtrue, endIfLabel);
                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, GetValueMethod);
                    generator.Emit(OpCodes.Unbox_Any, pi.PropertyType);
                    generator.Emit(OpCodes.Callvirt, pi.GetSetMethod());
                    generator.MarkLabel(endIfLabel);
                }
            }

            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);

            GetEntity<T> load = (GetEntity<T>)method.CreateDelegate(typeof(GetEntity<T>));


            while (collection.Read())
            {
                list.Add(load(collection));
            }

            return list;
        }


        //public T[] PopulateEntityFromCollection<T>(DataTable collection) where T : new()
        //{


        //    Type t = typeof(T);

        //    if (collection.Rows.Count == 0)
        //        return new T[0];

        //    DataRow dr = collection.Rows[0];

        //    DynamicMethod method = new DynamicMethod("DynamicCreateEntity", t, new Type[] { typeof(DataRow) }, t, true);
        //    ILGenerator generator = method.GetILGenerator();
        //    LocalBuilder result = generator.DeclareLocal(t);
        //    generator.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
        //    generator.Emit(OpCodes.Stloc, result);

        //    for (int i = 0; i < dr.ItemArray.Length; i++)
        //    {
        //        PropertyInfo pi = t.GetProperty(collection.Columns[i].ColumnName);
        //        System.Reflection.Emit.Label endIfLabel = generator.DefineLabel();
        //        if (pi != null && pi.GetSetMethod() != null)
        //        {
        //            generator.Emit(OpCodes.Ldarg_0);
        //            generator.Emit(OpCodes.Ldc_I4, i);
        //            generator.Emit(OpCodes.Callvirt, IsDBNullMethod);
        //            generator.Emit(OpCodes.Brtrue, endIfLabel);
        //            generator.Emit(OpCodes.Ldloc, result);
        //            generator.Emit(OpCodes.Ldarg_0);
        //            generator.Emit(OpCodes.Ldc_I4, i);
        //            generator.Emit(OpCodes.Callvirt, GetValueMethod);
        //            generator.Emit(OpCodes.Unbox_Any, pi.PropertyType);
        //            generator.Emit(OpCodes.Callvirt, pi.GetSetMethod());
        //            generator.MarkLabel(endIfLabel);
        //        }
        //    }

        //    generator.Emit(OpCodes.Ldloc, result);
        //    generator.Emit(OpCodes.Ret);

        //    GetEntity<T> load = (GetEntity<T>)method.CreateDelegate(typeof(GetEntity<T>));

        //    T[] list = new T[collection.Rows.Count];

        //    for (int i = 0; i < collection.Rows.Count; i++)
        //    {
        //        list[i] = load(collection.Rows[i]);
        //    }

        //    return list;
        //}

    }
}
